﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightingReflectionApp
{
    public class IlluminationCalculator
    {
        private Room room;
        private List<Lamp> lamps;
        public Surface Surface { get; private set; }
        public ReflectiveSurface ReflectiveSurface { get; private set; }

        public IlluminationCalculator(Room room, List<Lamp> lamps, Surface surface, ReflectiveSurface reflectiveSurface = null)
        {
            this.room = room;
            this.lamps = lamps;
            this.Surface = surface;
            this.ReflectiveSurface = reflectiveSurface;
        }

        public void CalculateIlluminationFromLamps()
        {
            for (int i = 0; i < Surface.Squares.GetLength(0); i++)
            {
                for (int j = 0; j < Surface.Squares.GetLength(1); j++)
                {
                    double x = Surface.Squares[i, j].X;
                    double y = Surface.Squares[i, j].Y;
                    double illumination = CalculateIlluminationAtPoint(x, y, Surface.Height);
                    Surface.Squares[i, j].Illumination = illumination;
                }
            }
        }

        public void CalculateReflectedIllumination()
        {
            for (int i = 0; i < Surface.Squares.GetLength(0); i++)
            {
                for (int j = 0; j < Surface.Squares.GetLength(1); j++)
                {
                    double reflectedIllumination = 0;

                    if (ReflectiveSurface != null)
                    {
                        var reflectiveSquare = ReflectiveSurface.Squares[i, j];
                        double d = Surface.Height - reflectiveSquare.HeightAboveFloor;
                        if (d >= 0 && reflectiveSquare.HeightAboveFloor>0)
                        {
                            double E0 = CalculateIlluminationAtPoint(reflectiveSquare.X, reflectiveSquare.Y, reflectiveSquare.HeightAboveFloor);
                            double k = reflectiveSquare.ReflectionCoefficient;
                            double Er = E0 * k * Math.Exp(-0.1*d);

                            reflectedIllumination += Er;
                        }
                    }

                    Surface.Squares[i, j].Illumination += reflectedIllumination;
                }
            }
        }

        private double CalculateIlluminationAtPoint(double x, double y, double height)
        {
            double totalIllumination = 0;

            foreach (var lamp in lamps)
            {
                double d = Math.Sqrt(Math.Pow(x - lamp.X, 2) + Math.Pow(y - lamp.Y, 2) + Math.Pow(height - lamp.Z, 2));
                double cosTheta = Math.Abs(height - lamp.Z) / d;
                double I = lamp.CalculateLuminousIntensity();
                double E = I * cosTheta / (d * d);

                totalIllumination += E;
            }

            return totalIllumination;
        }

        public void CalculateSecondaryReflectedIllumination()
        {
            //Шаг 1. Первичное отражение
            CalculateReflectedIllumination();
            //Шаг 2. Лучи доходят до потолка
            Surface ceilingSurface = new Surface(room.Height, room);
            CalculateParallelIllumination(this.Surface, ceilingSurface);
            //Шаг 3. Лучи отражаются от потолка
            CalculateDimming(ceilingSurface, room.CeilingReflectionCoefficient);
            //Шаг 4. Лучи падают на расчетную поверхность
            CalculateParallelIllumination(ceilingSurface, this.Surface);
            //Шаг 5. Лучи от расчетной поверхности падают на отражающую и обратно возвращаются на расчетную
            CalculatePingPong();
        }

        public static void CalculateParallelIllumination(Surface Surface1, Surface Surface2)
        {
            double d = Math.Abs(Surface1.Height - Surface2.Height);
            for (int i = 0; i < Surface1.Squares.GetLength(0); i++)
            {
                for (int j = 0; j < Surface1.Squares.GetLength(1); j++)
                {
                    Surface2.Squares[i, j].Illumination = Surface1.Squares[i, j].Illumination * Math.Exp(-0.1*d);
                }
            }
        }

        public static void CalculateDimming(Surface Surface1, double k)
        {
            for (int i = 0; i < Surface1.Squares.GetLength(0); i++)
            {
                for (int j = 0; j < Surface1.Squares.GetLength(1); j++)
                {
                    Surface1.Squares[i, j].Illumination *= k;
                }
            }
        }

        public void CalculatePingPong()
        {
            for (int i = 0; i < Surface.Squares.GetLength(0); i++)
            {
                for (int j = 0; j < Surface.Squares.GetLength(1); j++)
                {
                    var reflectiveSquare = ReflectiveSurface.Squares[i, j];
                    double d = Surface.Height - reflectiveSquare.HeightAboveFloor;
                    double E0 = Surface.Squares[i, j].Illumination;

                    if (d >= 0)
                    {
                        double rk = reflectiveSquare.ReflectionCoefficient;
                        double E = E0 * rk * Math.Exp(-0.1*2*d);
                        Surface.Squares[i, j].Illumination = E;
                    }
                    // else: освещенность не меняется, поэтому ничего не нужно делать
                }
            }
        }
    }
}
